#pragma once
#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <unistd.h>
#include <unordered_map>
#include <sys/timerfd.h>

#include "Channel.hpp"
#include "Log.hpp"

// 定时任务类
using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask
{
private:
    uint64_t _task_id;       // 任务id
    int _timeout;            // 超时时间
    bool _canceled;          // 是否取消定时任务
    TaskFunc _task_cb;       // 定时任务执行的回调函数
    ReleaseFunc _release_cb; // 定时任务销毁的回调函数

public:
    TimerTask(uint64_t task_id, int delay)
        : _task_id(task_id), _timeout(delay), _canceled(false) {}
    ~TimerTask()
    {
        _release_cb();
        if (_canceled == false)
            _task_cb();
    }

    // 取消定时任务
    void CancelTask() { _canceled = true; }
    // 获取定时任务的超时时间
    int GetTimeout() { return _timeout; }
    // 设置回调函数
    void SetTaskFunc(const TaskFunc &cb) { _task_cb = cb; }
    void SetReleaseFunc(const ReleaseFunc &cb) { _release_cb = cb; }
};

class EventLoop;

// 时间轮
using PtrTimerTask = std::shared_ptr<TimerTask>;
using WeakTimerTask = std::weak_ptr<TimerTask>;
class TimerWheel
{
private:
    int _tick;     // 滴答指针
    int _capacity; // 时间轮容量

    std::vector<std::vector<PtrTimerTask>> _wheel; // 时间轮
    std::unordered_map<int, WeakTimerTask> _timers;

    EventLoop *_loop;
    int _timerfd; // 定时器文件描述符
    std::unique_ptr<Channel> _timerfd_channel;

private:
    // 执行任务后定时任务把数据销毁的回调函数
    void ReleaseHashMap(int task_id)
    {
        auto it = _timers.find(task_id);
        if (it == _timers.end())
            return;
        _timers.erase(it);
    }

    // 创建一个Timerfd对象，并开始计时
    int CreateTimerFd()
    {
        int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (timerfd < 0)
        {
            ERR_LOG("create timerfd fail");
            abort();
        }

        // 设置顶定时器的超时时间为1s
        struct itimerspec itime;
        itime.it_value.tv_sec = 1;
        itime.it_value.tv_nsec = 0;
        itime.it_interval.tv_sec = 0;
        itime.it_interval.tv_nsec = 0;
        // 开始运行计时器
        timerfd_settime(timerfd, 0, &itime, nullptr);

        return timerfd;
    }
    // 读取timerfd文件描述符
    int ReadTimerFd()
    {
        uint64_t times = 0;
        ssize_t ret = read(_timerfd, &times, sizeof(times));
        if (ret <= 0)
        {
            if (errno == EINTR)
                return;
            ERR_LOG("read timerfd fail");
            abort();
        }
        return times;
    }
    // 滴答指针向后走一步
    void RunTimerWheel()
    {
        _tick = (_tick + 1) % _capacity;
        // 执行定时任务,clear会delete这些shared_ptr，之后会自动调用这些shared_ptr的析构函数
        _wheel[_tick].clear();
    }

    // 将滴答指针向后走一步和读取timerfd相结合
    // 因为可能程序的原因，系统忙不过来，当超时的时候，可能没有立即读取timerfd，让指针向后走一步
    // 所以当读取的时候，可能已经超时n次了，所以我们要根据返回值来确定调用几次RunTimerTask函数
    void OnTime()
    {
        int times = ReadTimerFd();
        for (size_t i = 0; i < times; i++)
        {
            RunTimerWheel(); // 指针向后走一步
        }
    }

    // 添加定时任务
    void TimerAddInLoop(uint64_t task_id, int delay, const TaskFunc &cb)
    {
        // 判断设置的超时时间是否在合理范围之内
        if (delay > 60 || delay < 0)
            return;
        // 如果有该任务，那么则什么也不做
        if (HasTiemrTask(task_id))
            return;

        // 添加任务
        // shared_ptr的构造函数: shared_ptr(T* ptr);
        PtrTimerTask pt(new TimerTask(task_id, delay)); //?
        pt->SetTaskFunc(cb);
        pt->SetReleaseFunc(std::bind(&TimerWheel::ReleaseHashMap, this, task_id));

        // 将任务的指针添加到时间轮和HashMap中
        // weak_ptr中有构造函数可以用shared_ptr构造weak_ptr ---> std::weak_ptr<TimeTask>(pt)
        _timers[task_id] = WeakTimerTask(pt);
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(pt);
    }
    // 刷新定时任务
    void TimerRefreshInLoop(uint64_t task_id)
    {
        auto it = _timers.find(task_id);
        if (it == _timers.end())
            return;

        // 构造一个新的shared_ptr一起指向该任务
        PtrTimerTask pt = it->second.lock();
        // 将shared_ptr放入时间轮中
        int pos = (_tick + pt->GetTimeout()) % _capacity;
        _wheel[pos].push_back(pt);
    }
    // 取消定时任务
    void TimerCancelInLoop(uint64_t task_id)
    {
        auto it = _timers.find(task_id);
        if (it == _timers.end())
            return;

        // 从weak_ptr中获取shared_ptr对象，用shared_ptr对象调用定时任务内部函数
        // weak_ptr中没有operator->、operator*
        PtrTimerTask pt = it->second.lock();
        if (pt)
            pt->CancelTask();
    }

public:
    TimerWheel(EventLoop *loop)
        : _tick(0), _capacity(60), _wheel(_capacity), _loop(loop), _timerfd(CreateTimerFd()), _timerfd_channel(new Channel(loop, _timerfd))
    {
        // 添加timerfd读事件回调
        _timerfd_channel->SetReadCallBack(std::bind(&TimerWheel::ReadTimerFd, this));
        // 开启timerfd读事件监控
        _timerfd_channel->EnableRead();
    }
    ~TimerWheel() = default;

    // 判断定时任务是否存在
    // 这个接口也是存在线程安全问题的，但不对外界使用者调用，只是在模块内部，在对应的EventLoop线程内部执行
    bool HasTiemrTask(uint64_t task_id)
    {
        auto it = _timers.find(task_id);
        if (it == _timers.end())
            return false;
        return true;
    }
    // 添加定时任务（并非真的添加，而是作为任务放入任务队列）
    void TimerAdd(uint64_t task_id, int delay, const TaskFunc &cb);
    // 刷新定时任务
    void TimerRefresh(uint64_t task_id);
    // 取消定时任务
    void TimerCancel(uint64_t task_id);
};